import 'package:aihu_getx_app/app/interceptors/login_intercept_chain.dart';
import 'package:aihu_getx_app/app/modules/commons/commons.dart';
import 'package:aihu_getx_app/app/modules/commons/model/client_base_config_model.dart';
import 'package:aihu_getx_app/app/modules/commons/model/common_http_resp_model.dart';
import 'package:aihu_getx_app/utils/constants.dart';
import 'package:aihu_getx_app/utils/custom_get_util.dart';
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:get/get.dart' as get_x;
import 'package:pretty_dio_logger/pretty_dio_logger.dart';

class Http {
  static Future<List<T>> getList<T>(
    String path, {
    Object? data,
    Map<String, dynamic>? queryParameters,
    Options? options,
    CancelToken? cancelToken,
    ProgressCallback? onReceiveProgress,
    required T Function(dynamic json) fromJsonT,
  }) async {
    Response<Map<String, dynamic>> t = await _getDio().get(
      path,
      data: data,
      queryParameters: queryParameters,
      options: options,
      cancelToken: cancelToken,
      onReceiveProgress: onReceiveProgress,
    );
    refreshLocalToken();
    return Future(
      () => CommonHttpRespModel<List<T>>.fromJson(
        t.data as Map<String, dynamic>,
        (json) => (json as List<dynamic>)
            .map((e) => fromJsonT(e as Map<String, dynamic>))
            .toList(),
      ).data as List<T>,
    );
  }

  static Future<T> get<T>(
    String path, {
    Object? data,
    Map<String, dynamic>? queryParameters,
    Options? options,
    CancelToken? cancelToken,
    ProgressCallback? onReceiveProgress,
    required T Function(dynamic json) fromJsonT,
  }) async {
    Response<Map<String, dynamic>> t = await _getDio().get(
      path,
      data: data,
      queryParameters: queryParameters,
      options: options,
      cancelToken: cancelToken,
      onReceiveProgress: onReceiveProgress,
    );
    refreshLocalToken();
    return Future(
      () => CommonHttpRespModel<T>.fromJson(
        t.data as Map<String, dynamic>,
        fromJsonT,
      ).data as T,
    );
  }

  static Future<T> post<T>(
    String path, {
    Object? data,
    Map<String, dynamic>? queryParameters,
    Options? options,
    CancelToken? cancelToken,
    ProgressCallback? onSendProgress,
    ProgressCallback? onReceiveProgress,
    required T Function(dynamic json) fromJsonT,
  }) async {
    if (null != options &&
        options.headers?["isLogin"] != null &&
        options.headers?["isLogin"]) {
      Http.instance.dio.options.headers.remove("Authorization");
    }
    Response<Map<String, dynamic>> t = await _getDio().post(
      path,
      data: data,
      queryParameters: queryParameters,
      options: options,
      cancelToken: cancelToken,
      onSendProgress: onSendProgress,
      onReceiveProgress: onReceiveProgress,
    );
    if (null != options &&
        options.headers?["isLogout"] != null &&
        options.headers?["isLogout"]) {
    } else {
      refreshLocalToken();
    }
    return Future(
      () => CommonHttpRespModel<T>.fromJson(
        t.data as Map<String, dynamic>,
        fromJsonT,
      ).data as T,
    );
  }

  static Future<T> put<T>(
    String path, {
    Object? data,
    Map<String, dynamic>? queryParameters,
    Options? options,
    CancelToken? cancelToken,
    ProgressCallback? onSendProgress,
    ProgressCallback? onReceiveProgress,
    required T Function(dynamic json) fromJsonT,
  }) async {
    Response<Map<String, dynamic>> t = await _getDio().put(
      path,
      data: data,
      queryParameters: queryParameters,
      options: options,
      cancelToken: cancelToken,
      onSendProgress: onSendProgress,
      onReceiveProgress: onReceiveProgress,
    );
    refreshLocalToken();
    return Future(
      () => CommonHttpRespModel<T>.fromJson(
        t.data as Map<String, dynamic>,
        fromJsonT,
      ).data as T,
    );
  }

  static Dio _getDio() {
    Dio inst = Http.instance.dio;
    inst.options.baseUrl = Commons.getCurrentClientConfig().serverApiUrl;
    return inst;
  }

  static Http get instance => Http();

  static final Http _instance = Http._();

  factory Http() => _instance;

  static late Dio _dio;

  Dio get dio => _dio;

  Http._() {
    ClientBaseConfigModel config = Commons.getCurrentClientConfig();
    // 构造 Dio options
    final BaseOptions options = BaseOptions(
      receiveTimeout: const Duration(seconds: 10),
      connectTimeout: const Duration(seconds: 10),
      sendTimeout: const Duration(seconds: 8),
      responseType: ResponseType.json,
      validateStatus: (_) => true,
      baseUrl: config.serverApiUrl,
      headers: {
        "Content-Type": "application/json",
        "Accept": "application/json",
        "tenant-id": config.tenantId,
        "app-id": config.appId,
      },
    );
    if (LoginInterceptChain.isLogin()) {
      options.headers.update(
        "Authorization",
        (dynamic val) => "Bearer ${Commons.getLoginResModel()?.accessToken}",
        ifAbsent: () => "Bearer ${Commons.getLoginResModel()?.accessToken}",
      );
    }
    _dio = Dio(options);
    if (kDebugMode) {
      // 日志
      _dio.interceptors.add(
        PrettyDioLogger(
          requestHeader: true,
          requestBody: true,
          responseBody: true,
          responseHeader: true,
          error: true,
          compact: true,
          maxWidth: 90,
        ),
      );
    }
    _dio.interceptors.add(HttpInterceptor());
  }

  BaseError getError(Object obj) {
    // 这里封装了一个 BaseError 类，会根据后端返回的code返回不同的错误类
    switch (obj.runtimeType) {
      case DioException _:
        if ((obj as DioException).type == DioExceptionType.badResponse) {
          final response = (obj).response;
          if (response?.statusCode == 401) {
            return NeedLogin();
          } else if (response?.statusCode == 403) {
            return NeedAuth();
          } else if (response?.statusCode == 408) {
            return UserNotExist();
          } else if (response?.statusCode == 409) {
            return PwdNotMatch();
          } else if (response?.statusCode == 405) {
            return UserNameEmpty();
          } else if (response?.statusCode == 406) {
            return PwdEmpty();
          } else {
            return OtherError(
              statusCode: response?.statusCode as int,
              statusMessage: response?.statusMessage as String,
            );
          }
        }
    }

    return OtherError(statusCode: -1, statusMessage: '未知错误');
  }

  static void refreshLocalToken() {
    // if (LoginInterceptChain.isLogin()) {
    //   var config = Commons.getCurrentClientConfig();
    //   _getDio().post(config.refreshTokenUri, queryParameters: {
    //     "refreshToken": Commons.getLoginResModel()?.refreshToken,
    //   }).then((resp) {
    //     var data = LoginResModel.fromJson(resp.data["data"]);
    //     if (config.idx.type == ClientType.d.type) {
    //       SpUtil.putObject(CacheKeys.adminLoginInfo, data)
    //           ?.then((value) => refreshHeaderToken());
    //     } else {
    //       SpUtil.putObject(CacheKeys.userLoginInfo, data)
    //           ?.then((value) => refreshHeaderToken());
    //     }
    //   });
    // }
  }

  static void refreshHeaderToken() {
    if (LoginInterceptChain.isLogin()) {
      Http.instance.dio.options.headers.update(
        "Authorization",
        (dynamic val) => "Bearer ${Commons.getLoginResModel()?.accessToken}",
        ifAbsent: () => "Bearer ${Commons.getLoginResModel()?.accessToken}",
      );
    } else {
      Http.instance.dio.options.headers.remove("Authorization");
    }
  }
}

class HttpInterceptor extends Interceptor {
  late DateTime startTime;
  late DateTime endTime;

  @override
  void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
    EasyLoading.show(status: "加载中...", maskType: EasyLoadingMaskType.black);
    startTime = DateTime.now();

    if (kDebugMode) {
      print("-------- Http Interceptor start --------");
    }
    return handler.next(options);
  }

  @override
  void onResponse(Response response, ResponseInterceptorHandler handler) {
    EasyLoading.dismiss();
    endTime = DateTime.now();
    int duration = endTime.difference(startTime).inMilliseconds;

    if (kDebugMode) {
      print("-------- Http Interceptor end: $duration 毫秒 --------");
    }
    if (response.statusCode! > 299 || response.statusCode! < 200) {
      throw DioException(
          requestOptions: RequestOptions(),
          type: DioExceptionType.badResponse,
          response: response,
          message: response.statusMessage);
    }

    CommonHttpRespModel data = CommonHttpRespModel.fromJson(
      response.data,
      (json) => null,
    );
    if (data.code != 0) {
      CustomGetUtil.snackbar("提示", data.msg ?? "未知错误");
      if (data.code == 401) {
        get_x.Get.offAllNamed(
          LoginInterceptChain.logout(),
          arguments: {
            Constants.jumpArgument: get_x.Get.currentRoute,
          },
        );
      }
      return;
    }

    return handler.next(response);
  }

  @override
  void onError(DioException err, ErrorInterceptorHandler handler) {
    EasyLoading.dismiss();
    super.onError(err, handler);
    if (err.type == DioExceptionType.connectionError ||
        err.type == DioExceptionType.connectionTimeout) {
      CustomGetUtil.snackbar("提示", '网络错误');
      if (kDebugMode) {
        print('网络错误: ${err.message}');
      }
    } else {
      CustomGetUtil.snackbar(
          "提示", '请求错误: ${err.response?.statusCode}-${err.message}');
      if (kDebugMode) {
        print('请求错误: ${err.response?.statusCode}-${err.message}');
      }
    }
  }
}

abstract class BaseError {
  final int code;
  final String message;

  BaseError({required this.code, required this.message});
}

class NeedLogin implements BaseError {
  @override
  int get code => 401;

  @override
  String get message => "请先登录";
}

class NeedAuth implements BaseError {
  @override
  int get code => 403;

  @override
  String get message => "非法访问，请使用正确的token";
}

class UserNotExist implements BaseError {
  @override
  int get code => 408;

  @override
  String get message => "用户不存在";
}

class UserNameEmpty implements BaseError {
  @override
  int get code => 405;

  @override
  String get message => "用户名不能为空";
}

class PwdNotMatch implements BaseError {
  @override
  int get code => 409;

  @override
  String get message => "用户密码不正确";
}

class PwdEmpty implements BaseError {
  @override
  int get code => 406;

  @override
  String get message => "用户密码不能为空";
}

class OtherError implements BaseError {
  final int statusCode;
  final String statusMessage;

  OtherError({required this.statusCode, required this.statusMessage});

  @override
  int get code => statusCode;

  @override
  String get message => statusMessage;
}
